package com.muyan.sys.common.utils;

import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.BCUtil;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.SmUtil;
import cn.hutool.crypto.asymmetric.KeyType;
import cn.hutool.crypto.asymmetric.SM2;
import cn.hutool.crypto.symmetric.SM4;
import cn.hutool.crypto.symmetric.SymmetricCrypto;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;

import java.util.HashMap;
import java.util.Map;


/**
 * 国密算法 工具类
 * sm2加密的坑 私钥前加00，密文前加04，公钥前加04
 * 确保生成的公钥是04开头 私钥和密文 代码已经处理
 */
public class SMUtils {

    /**
     * 默认 KEY
     * 用于SM4加密
     * key必须是16位 可包含字母、数字、标点
     */
    public static final String DEFAULT_KEY = "MYKJjszx@#$%YFZX";

    /**
     * 默认 IV
     * 当使用CBC模式的时候，KEY和IV都需要传值，解密要和加密的KEY和IV一致，更加安全
     * iv 必须为 16 位，可包含字母、数字、标点
     */
    public static final String DEFAULT_IV = "mykj=987GXUC8801";

    /**
     * SM2 非对称加密，基于ECC。
     * 生成 SM2 公私钥
     *
     * @return Map<String, String>
     */
    public static Map<String, Object> generateSM2Key() {
        SM2 sm2 = SmUtil.sm2();
        String privateKey = HexUtil.encodeHexStr(BCUtil.encodeECPrivateKey(sm2.getPrivateKey()));
        String publicKey = HexUtil.encodeHexStr(((BCECPublicKey) sm2.getPublicKey()).getQ().getEncoded(false));
        Map<String, Object> map = new HashMap<>(2);
        map.put("publicKey", publicKey);
        map.put("privateKey", privateKey);
        return map;
    }

    /**
     * SM2 非对称加密，基于ECC。
     * 通过 SM2 公钥加密
     *
     * @param str       需要加密的字符串
     * @param publicKey 公钥
     * @return /
     */
    public static String encryptSM2(String str, String publicKey) {
        if (publicKey.length() == 130) {
            //这里需要去掉开始第一个字节 第一个字节表示标记
            publicKey = publicKey.substring(2);
        }
        String xhex = publicKey.substring(0, 64);
        String yhex = publicKey.substring(64, 128);
        ECPublicKeyParameters ecPublicKeyParameters = BCUtil.toSm2Params(xhex, yhex);
        //利用公钥加密
        SM2 sm2 = SmUtil.sm2();
        sm2.setPublicKeyParams(ecPublicKeyParameters);
        return sm2.encryptBcd(str, KeyType.PublicKey);
    }

    /**
     * SM2 非对称加密，基于ECC。
     * 通过 SM2 私钥解密
     *
     * @param str        需要解密的字符串
     * @param privateKey 私钥
     * @return /
     */
    public static String decryptSM2(String str, String privateKey) {
        // sm2加密的坑 私钥前加00，密文前加04，公钥前加04
        if (!str.startsWith("04")) {
            str = "04".concat(str);
        }
        if (!privateKey.startsWith("00")) {
            privateKey = "00".concat(privateKey);
        }
        ECPrivateKeyParameters privateKeyParameters = BCUtil.toSm2Params(privateKey);
        SM2 sm2 = SmUtil.sm2();
        sm2.setPrivateKeyParams(privateKeyParameters);
        return StrUtil.utf8Str(sm2.decryptFromBcd(str, KeyType.PrivateKey));
    }

    /**
     * SM3加密(国产哈希算法) 消息摘要
     * 可以用MD5作为对比理解。
     *
     * @param data 加密字符串
     */
    public static String getSM3(String data) {
        return SmUtil.sm3(data);
    }

    /**
     * SM3加密(国产哈希算法) 默认大写
     * 消息摘要 可以用MD5作为对比理解。
     *
     * @param data        加密字符串
     * @param toUpperCase 是否转大写
     */
    public static String getSM3(String data, boolean toUpperCase) {
        if (toUpperCase) {
            return SmUtil.sm3(data).toUpperCase();
        }
        return SmUtil.sm3(data);
    }

    /**
     * SM4加密 使用默认密钥
     * SM4 无线局域网标准的分组数据算法 对称加密
     * 默认返回大写
     *
     * @param data 需要加解密字符串
     * @return 处理结果
     */
    public static String encryptSM4(String data) {
        return encryptSM4(data, null);
    }
    /**
     * SM4加密 使用默认密钥
     * SM4 无线局域网标准的分组数据算法 对称加密
     * 默认返回大写
     *
     * @param data 需要加解密字符串
     * @return 处理结果
     */
    public static String encryptSM4Defultkey(String data) {
        return encryptSM4(data, DEFAULT_KEY);
    }
    /**
     * SM4解密 使用默认密钥
     * SM4 无线局域网标准的分组数据算法 对称加密
     *
     * @param data 需要加解密字符串
     * @return 处理结果
     */
    public static String decryptSM4DefultKey(String data) {
        if(StringUtils.isEmpty(data)){
            return "";
        }
        return decryptSM4(data, DEFAULT_KEY);
    }
    /**
     * SM4解密 使用默认密钥
     * SM4 无线局域网标准的分组数据算法 对称加密
     *
     * @param data 需要加解密字符串
     * @return 处理结果
     */
    public static String decryptSM4(String data) {
        return decryptSM4(data, null);
    }

    /**
     * SM4加密 CBC模式 使用默认密钥 默认IV
     * SM4 无线局域网标准的分组数据算法 对称加密
     * 默认返回大写
     *
     * @param data 需要加解密字符串
     * @return 处理结果
     */
    public static String encryptSM4CBC(String data) {
        return encryptSM4CBC(data, null, null);
    }

    /**
     * SM4解密 CBC模式 使用默认密钥 默认IV
     * SM4 无线局域网标准的分组数据算法 对称加密
     *
     * @param data 需要加解密字符串
     * @return 处理结果
     */
    public static String decryptSM4CBC(String data) {
        return decryptSM4CBC(data, null, null);
    }

    /**
     * SM4加密 自定义密钥
     * SM4 无线局域网标准的分组数据算法 对称加密
     * 默认返回大写
     *
     * @param data 需要加解密字符串
     * @param key  自定义密钥 key必须是128位，也就是16个字节 可为null，null使用默认key值
     * @return 处理结果
     */
    public static String encryptSM4(String data, String key) {
        if (StrUtil.isEmpty(key)) {
            key = DEFAULT_KEY;
        }
        SymmetricCrypto sm4 = SmUtil.sm4(key.getBytes(CharsetUtil.CHARSET_UTF_8));
        return sm4.encryptHex(data).toUpperCase();
    }

    /**
     * SM4解密 自定义密钥
     * SM4 无线局域网标准的分组数据算法 对称加密
     *
     * @param data 需要加解密字符串
     * @param key  自定义密钥 key必须是128位，也就是16个字节 可为null，null使用默认key值
     * @return 处理结果
     */
    public static String decryptSM4(String data, String key) {
        if (StrUtil.isEmpty(key)) {
            key = DEFAULT_KEY;
        }
        SymmetricCrypto sm4 = SmUtil.sm4(key.getBytes(CharsetUtil.CHARSET_UTF_8));
        return sm4.decryptStr(data, CharsetUtil.CHARSET_UTF_8);
    }

    /**
     * SM4加密 自定义密钥 CBC模式
     * SM4 无线局域网标准的分组数据算法 对称加密
     * 默认返回大写
     *
     * @param data 需要加解密字符串
     * @param key  自定义密钥 key必须是128位，也就是16个字节 可为null，null使用默认key值
     * @return 处理结果
     */
    public static String encryptSM4CBC(String data, String key, String iv) {
        if (StrUtil.isEmpty(key)) {
            key = DEFAULT_KEY;
        }
        if (StrUtil.isEmpty(iv)) {
            iv = DEFAULT_IV;
        }
        SymmetricCrypto sm4 = new SM4(Mode.CBC, Padding.PKCS5Padding, key.getBytes(CharsetUtil.CHARSET_UTF_8), iv.getBytes(CharsetUtil.CHARSET_UTF_8));
        return sm4.encryptHex(data).toUpperCase();
    }

    /**
     * SM4解密 自定义密钥 CBC模式
     * SM4 无线局域网标准的分组数据算法 对称加密
     *
     * @param data 需要加解密字符串
     * @param key  自定义密钥 key必须是128位，也就是16个字节 可为null，null使用默认key值
     * @return 处理结果
     */
    public static String decryptSM4CBC(String data, String key, String iv) {
        if (StrUtil.isEmpty(key)) {
            key = DEFAULT_KEY;
        }
        if (StrUtil.isEmpty(iv)) {
            iv = DEFAULT_IV;
        }
        SymmetricCrypto sm4 = new SM4(Mode.CBC, Padding.PKCS5Padding, key.getBytes(CharsetUtil.CHARSET_UTF_8), iv.getBytes(CharsetUtil.CHARSET_UTF_8));
        return sm4.decryptStr(data, CharsetUtil.CHARSET_UTF_8);
    }
}
